package com.leff.cards;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.CompositeFilterOperator;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.leff.cards.entity.Configuration;
import com.leff.cards.entity.EntityWrapper;
import com.leff.cards.entity.Group;
import com.leff.cards.entity.Invitation;
import com.leff.cards.entity.QueryCard;
import com.leff.cards.entity.ResponseCard;
import com.leff.cards.entity.User;
import com.leff.cards.request.Request.Parameter;

public class StoreWrapper {

	private static final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
	private static final String KEY = "__key__";
	
	public static void store(EntityWrapper E) {
		datastore.put(E.getEntity());
	}
	public static void delete(EntityWrapper E) {
		datastore.delete(E.getKey());
	}
	
	private static HashMap<String, Integer> countMap = new HashMap<String, Integer>();
	private static ArrayList<QueryCard> allQueries;
	private static ArrayList<ResponseCard> allResponses;
	
	/*
	 * USER STUFF
	 */
	public static User getOrCreateUserByFbId(long fbid, String name) {
		
		User U = getUserByFbId(fbid);
		if(U == null) {
			U = new User(fbid, name);
			store(U);
			return U;
		}
		return U;
	}
	public static User getUserByFbId(long fbid) {
		Query q = new Query(User.KIND);
		q.setFilter(new FilterPredicate(Parameter.FB_ID, Query.FilterOperator.EQUAL, fbid));
		PreparedQuery pq = datastore.prepare(q);
		
		Entity E = pq.asSingleEntity();
		if(E == null) {
			return null;
		}
		return new User(E);
	}
	public static User getUserById(long id) {
		Entity E = StoreWrapper.getEntityById(User.KIND, id);
		return new User(E);
	}
	public static ArrayList<User> getUsersById(List<Long> ids) {

		if(ids.size() == 0) {
			return new ArrayList<User>();
		}
		Query q = new Query(User.KIND);
		ArrayList<Key> keys = new ArrayList<Key>();
		for(Long L : ids) {
			keys.add(KeyFactory.createKey(User.KIND, L));
		}
		q.setFilter(new FilterPredicate(KEY, Query.FilterOperator.IN, keys));
		
		PreparedQuery pq = datastore.prepare(q);
		
		List<Entity> entities = pq.asList(FetchOptions.Builder.withDefaults());
		
		ArrayList<User> users = new ArrayList<User>();
		for(Entity E : entities) {
			users.add(new User(E));
		}
		
		return users;
	}
	
	/*
	 * QUERY_CARD STUFF
	 */
	public static QueryCard getQueryCardById(long id) {
		Entity E = StoreWrapper.getEntityById(QueryCard.KIND, id);
		return new QueryCard(E);
	}
	
	public static ArrayList<QueryCard> getAllQueryCards() {
		
		if(allQueries != null) {
			return allQueries;
		}
		Query Q = new Query(QueryCard.KIND);
		PreparedQuery pq = datastore.prepare(Q);
		
		allQueries = new ArrayList<QueryCard>();
		
		Iterator<Entity> it = pq.asIterator();
		while(it.hasNext()) {
			allQueries.add(new QueryCard(it.next()));
		}
		
		return allQueries;
	}
	
	/*
	 * RESPONSE_CARD STUFF
	 */	
	public static ResponseCard getResponseCardById(long id) {
		Entity E = StoreWrapper.getEntityById(ResponseCard.KIND, id);
		return new ResponseCard(E);
	}
	
	public static ArrayList<ResponseCard> getResponseCardsWithIdString(String idString) {
		
		String[] stringIdArray = idString.split(",");
		long[] idArray = new long[stringIdArray.length];
		for(int i = 0; i < stringIdArray.length; i++) {
			idArray[i] = Long.valueOf(stringIdArray[i]);
		}
		
		ArrayList<ResponseCard> cards = new ArrayList<ResponseCard>();
		
		ArrayList<Key> keys = new ArrayList<Key>();
		for(long id : idArray) {
			keys.add(KeyFactory.createKey(ResponseCard.KIND, id));
		}
		
		Query q = new Query(ResponseCard.KIND);
		q.setFilter(new FilterPredicate(KEY, Query.FilterOperator.IN, keys));
		
		PreparedQuery pq = datastore.prepare(q);
		
		List<Entity> entities = pq.asList(FetchOptions.Builder.withDefaults());
		
		for(Entity E : entities) {
			cards.add(new ResponseCard(E));
		}
		
		return cards;
	}
	
	public static ArrayList<ResponseCard> getAllResponseCards() {
		
		if(allResponses != null) {
			return allResponses;
		}
		Query Q = new Query(ResponseCard.KIND);
		PreparedQuery pq = datastore.prepare(Q);
		
		allResponses = new ArrayList<ResponseCard>();
		
		Iterator<Entity> it = pq.asIterator();
		while(it.hasNext()) {
			allResponses.add(new ResponseCard(it.next()));
		}
		
		return allResponses;
	}
	
	/*
	 * GROUP STUFF
	 */
	public static ArrayList<Group> getGroupsForUser(long userId) {
		
		Query q = new Query(Group.KIND);
		q.setFilter(new FilterPredicate(Group.MEMBERS, Query.FilterOperator.EQUAL, userId));
		PreparedQuery pq = datastore.prepare(q);
		
		List<Entity> list = pq.asList(FetchOptions.Builder.withDefaults());
		ArrayList<Group> groups = new ArrayList<Group>();

		for(Entity E : list) {
			groups.add(new Group(E));
		}
		
		return groups;
	}
	
	public static Group getGroupById(long groupId) {
		Entity E = StoreWrapper.getEntityById(Group.KIND, groupId);
		return new Group(E);
	}
	
	/*
	 * INVITES STUFF
	 */
	public static ArrayList<Group> getInvitesForFbId(long fbid) {
		
		Query q = new Query(Invitation.KIND);
		q.setFilter(new FilterPredicate(Invitation.USER_FB_ID, Query.FilterOperator.EQUAL, fbid));
		
		PreparedQuery pq = datastore.prepare(q);
		List<Entity> ents = pq.asList(FetchOptions.Builder.withDefaults());
		
		ArrayList<Group> invites = new ArrayList<Group>();
		for(Entity E : ents) {
			Invitation I = new Invitation(E);
			Group G = getGroupById(I.getGroupId());
			invites.add(G);
		}
		
		return invites;
	}
	
	public static boolean inivitationExists(long groupId, long fbid) {
		
		Invitation I = getInvitation(groupId, fbid);
		return I != null;
	}
	
	public static void deleteInvitation(long groupId, long fbid) {
		
		Invitation I = getInvitation(groupId, fbid);
		if(I == null) {
			return;
		}
		datastore.delete(I.getKey());
	}
	
	private static Invitation getInvitation(long groupId, long fbid) {
		
		Query q = new Query(Invitation.KIND);
		q.setFilter(CompositeFilterOperator.and(
				new FilterPredicate(Invitation.GROUP_ID, Query.FilterOperator.EQUAL, groupId),
				new FilterPredicate(Invitation.USER_FB_ID, Query.FilterOperator.EQUAL, fbid)
				)
			);
		
		PreparedQuery pq = datastore.prepare(q);
		Entity E = pq.asSingleEntity();
		if(E == null) {
			return null;
		}
		return new Invitation(E);
	}
	
	public static Configuration getConfiguration() {
		
		Query q = new Query(Configuration.KIND);
		PreparedQuery pq = datastore.prepare(q);
		
		Entity E = pq.asSingleEntity();
		Configuration config;
		if(E == null) {
			config = new Configuration();
			store(config);
		} else {
			config = new Configuration(E);
		}
		
		return config;
	}
	
	/*
	 * UTILITIES
	 */
	public static int countKind(String kind) {
		
		if(countMap.containsKey(kind)) {
			return countMap.get(kind);
		}
		
		Query q = new Query(kind);
		PreparedQuery pq = datastore.prepare(q);
		
		int count = pq.countEntities(FetchOptions.Builder.withDefaults());
		countMap.put(kind, count);
		return count;
	}
	
	private static Entity getEntityById(String kind, long id) {
		
		Key key = KeyFactory.createKey(kind, id);
		
		Entity E = null;
		try {
			E = datastore.get(key);
		} catch(Exception e) {}
		
		return E;
	}
	
	public static String bindCardIdStrings(String[] cardIds) {
		StringBuilder sb = new StringBuilder();
		boolean first = true;
		for(String id : cardIds) {
			if(!first) {
				sb.append(",");
			}
			sb.append(id);
			first = false;
		}
		return sb.toString();
	}
	
	public static ArrayList<String> explodeCommaSeparatedParameter(ArrayList<String> cardStrings) {
		
		ArrayList<String> cardIds = new ArrayList<String>();
		
		for(String hand : cardStrings) {
			
			String[] cards = hand.split(",");
			for(String card : cards) {
				cardIds.add(card);
			}
		}
		
		return cardIds;
	}
}
